<html>
<script>
var length = 10;
function fn() {
  console.log(this.length);
}

var obj = {
  length: 5,
  method: function(fn) {
    fn();
    arguments[0]();
  }
};

obj.method(fn, 1); 

function fn2(a) {
  console.log(a); 
  var a = 2;
  function a() {}
  console.log(a); 
}

fn2(1);

var f = true;
if (f === true) {
  var a1 = 10;
}

function fn() {
  var b = 20;
  c = 30;
}

fn();
/*
很长一段时间我都以为{...}内的新声明的变量是局部变量，后来我才发现function内的新声明的变量才是局部变量，而没有用var声明的变量在哪里都是全局变量。再次提醒切记只有function(){}内新声明的才能是局部变量，while{...}、if{...}、for(..) 之内的都是全局变量（除非本身包含在function内）。
*/
console.log(a1); // 10
// console.log(b); // fn内的局部变量，外面访问不到。
// console.log(c);
/*
其实{...}内的变量也会提前声明。于是代码还没执行前，a变量已经被声明，于是 'a' in window 返回true，a被赋值。
*/
if('a2' in window) {
  var a2 = 10;
}

// alert(a2);

/* Jerry 这道题弄过，装箱和拆箱。
var a = 10;
a.pro = 10; // 这个属性只有一瞬间起作用。下一句的a是一个新的对象了，所以pro属性为undefined
console.log(a.pro + a);

var s = 'hello';
s.pro = 'world';
console.log(s.pro + s);
*/

console.log(typeof fn); // function
var fn;
function fn() {};
/*
因为函数声明优于变量声明。我们知道在代码逐行执行前，函数声明和变量声明会提前进行，而函数声明又会优于变量声明，这里的优于可以理解为晚于变量声明后，如果函数名和变量名相同，函数声明就能覆盖变量声明。所以以上代码将函数声明和变量声明调换顺序还是一样结果。
*/

</script>

<script>
/*
回文是指把相同的词汇或句子，在下文中调换位置或颠倒过来，产生首尾回环的情趣，叫做回文，也叫回环。比如 mamam redivider .
*/
function checkPalindrom(str) {  
    return str == str.split('').reverse().join('');
}

var a = "i042416";
var b = "i04240i";
console.log(checkPalindrom(a), checkPalindrom(b)); // false, true

function removeDuplicate(arr){
  let hashTable = {};
  let data = [];
  for(let i=0,l=arr.length;i<l;i++) {
    if(!hashTable[arr[i]]) {
      hashTable[arr[i]] = true;
      data.push(arr[i]);
    }
  }
  return data;
}

var a = removeDuplicate([1,13,24,11,11,14,1,2,2]);
console.log(a);

function findMaxDuplicateChar(str) {  
  if(str.length == 1) {
    return str;
  }
  let charObj = {};
  for(let i=0;i<str.length;i++) {
    if(!charObj[str.charAt(i)]) {
      charObj[str.charAt(i)] = 1;
    }else{
      charObj[str.charAt(i)] += 1;
    }
  }
  let maxChar = '',
      maxValue = 1;
  for(var k in charObj) {
    if(charObj[k] >= maxValue) {
      maxChar = k;
      maxValue = charObj[k];
    }
  }
  return maxChar;
}

console.log(findMaxDuplicateChar("afjghdfraaaasdenas"));

function getFibonacci(n) {  
  var fibarr = [];
  var i = 0;
  while(i<n) {
    if(i<=1) {
      fibarr.push(i);
    }else{
      fibarr.push(fibarr[i-1] + fibarr[i-2])
    }
    i++;
  }
 
  return fibarr;
}

console.log(getFibonacci(10));

function getMaxProfit(arr) {
 
    var minPrice = arr[0];
    var maxProfit = 0;
 
    for (var i = 0; i < arr.length; i++) {
        var currentPrice = arr[i];
 
        minPrice = Math.min(minPrice, currentPrice);
 
        var potentialProfit = currentPrice - minPrice;
 
        maxProfit = Math.max(maxProfit, potentialProfit);
    }
 
    return maxProfit;
}

console.log(getMaxProfit([10,5,11,7,8,9]));

function randomString(n) {  
  let str = 'abcdefghijklmnopqrstuvwxyz9876543210';
  let tmp = '',
      i = 0,
      l = str.length;
  for (i = 0; i < n; i++) {
    tmp += str.charAt(Math.floor(Math.random() * l));
  }
  return tmp;
}
console.log(randomString(6));
/*
自己实现一个函数，查找某个DOM节点下面的包含某个class的所有DOM节点？不允许使用原生提供的 getElementsByClassName querySelectorAll 等原生提供DOM查找函数。
*/

function queryClassName(node, name) {  
  var starts = '(^|[ \n\r\t\f])',
       ends = '([ \n\r\t\f]|$)';
  var array = [],
        regex = new RegExp(starts + name + ends),
        elements = node.getElementsByTagName("*"),
        length = elements.length,
        i = 0,
        element;
 
    while (i < length) {
        element = elements[i];
        if (regex.test(element.className)) {
            array.push(element);
        }
 
        i += 1;
    }
 
    return array;
}


function Foo() {
    getName = function () { 
      alert (1); 
    };
    return this;
}

Foo.getName = function () { 
  alert (2);
};

Foo.prototype.getName = function () { 
  alert (3);
};

var getName = function () { 
  alert (4);
};

function getName() { 
  alert (5);
}

debugger;
/*

第一问

先看此题的上半部分做了什么，首先定义了一个叫Foo的函数，之后为Foo创建了一个叫getName的静态属性存储了一个匿名函数，之后为Foo的原型对象新创建了一个叫getName的匿名函数。之后又通过函数变量表达式创建了一个getName的函数，最后再声明一个叫getName函数。

第一问的 Foo.getName 自然是访问Foo函数上存储的静态属性，自然是2，没什么可说的。
*/

Foo.getName(); // 2
/*第二问，直接调用 getName 函数。既然是直接调用那么就是访问当前上文作用域内的叫getName的函数，所以跟1 2 3都没什么关系。此题有无数面试者回答为5。此处有两个坑，一是变量声明提升，二是函数表达式。

变量声明提升

即所有声明变量或声明函数都会被提升到当前函数的顶部。

例如下代码:

console.log('x' in window);//true
var x;
x = 0;

代码执行时js引擎会将声明语句提升至代码最上方，变为：

var x;
console.log('x' in window);//true
x = 0;

函数表达式

var getName 与 function getName 都是声明语句，区别在于 var getName 是函数表达式，而 function getName 是函数声明。
函数表达式最大的问题，在于js会将此代码拆分为两行代码分别执行。

例如下代码：

console.log(x);//输出：function x(){}
var x=1;
function x(){}

实际执行的代码为，先将 var x=1 拆分为 var x; 和 x = 1; 两行，再将 var x; 和 function x(){} 两行提升至最上方变成：

var x;
function x(){}
console.log(x);
x=1;

所以最终函数声明的x覆盖了变量声明的x，log输出为x函数。
同理，原题中代码最终执行时的是：

function Foo() {
    getName = function () { alert (1); };
    return this;
}
var getName;//只提升变量声明
function getName() { alert (5);}//提升函数声明，覆盖var的声明
 
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
getName = function () { alert (4);};//最终的赋值再次覆盖function getName声明
 
getName();//最终输出4
*/
getName(); // 4
/*
第三问的 Foo().getName(); 先执行了Foo函数，然后调用Foo函数的返回值对象的getName属性函数。

Foo函数的第一句  getName = function () { alert (1); };  是一句函数赋值语句，注意它没有var声明，所以先向当前Foo函数作用域内寻找getName变量，没有。再向当前函数作用域上层，即外层作用域内寻找是否含有getName变量，找到了，也就是第二问中的alert(4)函数，将此变量的值赋值为 function(){alert(1)}。

此处实际上是将外层作用域内的getName函数修改了。

注意：此处若依然没有找到会一直向上查找到window对象，若window对象中也没有getName属性，就在window对象中创建一个getName变量。

之后Foo函数的返回值是this，而JS的this问题博客园中已经有非常多的文章介绍，这里不再多说。

简单的讲，this的指向是由所在函数的调用方式决定的。而此处的直接调用方式，this指向window对象。

遂Foo函数返回的是window对象，相当于执行 window.getName() ，而window中的getName已经被修改为alert(1)，所以最终会输出1

此处考察了两个知识点，一个是变量作用域问题，一个是this指向问题。
*/
Foo().getName();

/*
第四问

直接调用getName函数，相当于 window.getName() ，因为这个变量已经被Foo函数执行时修改了，遂结果与第三问相同，为1
*/

getName();
/*
此处考察的是js的运算符优先级问题
参考链接：https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
通过查上表可以得知点（.）的优先级高于new操作，遂相当于是:

new (Foo.getName)();

所以实际上将getName函数作为了构造函数来执行，遂弹出2。

*/
new Foo.getName();

/*
首先看运算符优先级括号高于new，实际执行为

(new Foo()).getName()
遂先执行Foo函数，而Foo此时作为构造函数却有返回值，所以这里需要说明下js中的构造函数返回值问题。
而在js中构造函数可以有返回值也可以没有。

1、没有返回值则按照其他语言一样返回实例化对象。
function F(){}
new F();
output: F{}
2、若有返回值则检查其返回值是否为引用类型。如果是非引用类型，如基本类型（string,number,boolean,null,undefined）则与无返回值相同，实际返回其实例化对象。

[图片]

3、若返回值是引用类型，则实际返回值为这个引用类型。

[图片]

原题中，返回的是this，而this在构造函数中本来就代表当前实例化对象，遂最终Foo函数返回实例化对象。

之后调用实例化对象的getName函数，因为在Foo构造函数中没有为实例化对象添加任何属性，遂到当前对象的原型对象（prototype）中寻找getName，找到了。

遂最终输出3。

*/
new Foo().getName();
/*
第七问, new new Foo().getName(); 同样是运算符优先级问题。

最终实际执行为：

new ((new Foo()).getName)();

先初始化Foo的实例化对象，然后将其原型上的getName函数作为构造函数再次new。

遂最终结果为3
*/

new new Foo().getName();
</script>
</html>